import { Moniter, MoniterMap } from './moniter'

type ValueToArg<T> = {
  [K in keyof T]: (nv: T[K] | null, ov: T[K] | null) => void
}

export interface ReactiveMap {}

export class Reactive<T extends ReactiveMap> extends Moniter<ValueToArg<T>> {
  // @ts-ignore
  public $data: T = {}

  public constructor(data?: T) {
    super()
    data && this.$defines(data)
  }

  /**
   * 批量定义属性
   * @param data 由属性构成的对象
   * @returns
   */
  public $defines(data: T) {
    const props = Object.keys(data) as (keyof T)[]
    props.forEach((prop) => {
      this.$define(prop, data[prop])
    })
    return this
  }

  /**
   * 定义属性
   * @param prop 属性名
   * @param defVal 默认值
   * @returns
   */
  public $define<K extends keyof T>(prop: K, defVal: T[K] | null) {
    if (Reflect.has(this, prop)) {
      Reflect.set(this.$data, prop, defVal)
    } else {
      let temp = defVal
      Reflect.defineProperty(this.$data, prop, {
        enumerable: true,
        configurable: true,
        get() {
          return temp
        },
        set: (value: T[K] | null) => {
          if (value !== temp) {
            const old = temp
            temp = value
            // @ts-ignore
            this.$emit(prop, value, old)
          }
        },
      })
      Reflect.defineProperty(this, prop, {
        enumerable: true,
        configurable: true,
        get() {
          return temp
        },
        set(this: Reactive<ReactiveMap>, value) {
          Reflect.set(this.$data, prop, value)
        },
      })
      // @ts-ignore
      this.$emit(prop, defVal, null)
    }
    return this
  }

  /**
   * 定义/设置 属性
   * @param prop 属性名
   * @param value 属性值
   * @returns
   */
  public $set<K extends keyof T>(prop: K, value: T[K]) {
    if (Reflect.has(this.$data, prop)) {
      this.$data[prop] = value
    } else {
      this.$define(prop, value)
    }
    return this
  }

  /**
   * 删除指定属性
   * @param prop 属性名
   * @returns
   */
  public $delete<K extends keyof T>(prop: K) {
    Reflect.set(this.$data, prop, null)
    this.$clear(prop)
    Reflect.deleteProperty(this, prop)
    Reflect.deleteProperty(this.$data, prop)
    return this
  }

  /**
   * 监听另一个 Reactive 实例中指定 prop 变化并同步到到当前实例中
   * @param target ReactiveInstance
   * @param props 被监听的属性
   */
  // public $watch<M>(target: ReactiveInstance<M>, ...props: Extract<keyof T, keyof M>[]) {
  //   props.forEach((prop) => {
  //     target.$on(prop, (nv) => {
  //       // @ts-ignore
  //       this.$set(prop, nv)
  //     })
  //   })
  // }

  public $depend<K extends keyof T>(depend: K[], cb: () => void, immediate: boolean = false, once: boolean = false) {
    depend.forEach((prop) => {
      this.$on(prop, cb, once)
    })
    immediate && cb()
    return this
  }

  /**
   * $on 的升级版，绑定监听时立即执行回调函数
   * @param prop 属性名
   * @param cb 回调函数
   * @param once 一次性函数
   * @returns
   */
  public $immediate<K extends keyof T>(prop: K, cb: (nv: T[K] | null, ov: T[K] | null) => void, once: boolean = false) {
    this.$on(prop, cb, once)
    let value = Reflect.get(this, prop)
    if (value === undefined) {
      value = null
    }
    cb(value, value)
    return this
  }
}

export type ReactiveInstance<T> = T & Reactive<T>

export function reactive<T extends ReactiveMap>(data?: T) {
  return new Reactive(data) as ReactiveInstance<T>
}
